<!--
 * @LastEditTime: 2023-11-23 16:12:32
 * @FilePath: \bdcar-net\src\views\trajectoryMonitor\realtimePosition\index.vue
-->
<template>
  <div class="realtimePosition" v-loading="mapLoading">
    <!-- 地图 -->
    <div id="rpMap" class="myMap"></div>
    <div class="refresh-text">{{ countDown }}秒后更新</div>

    <!-- 工具栏 -->
    <mapTool class="leftTopTool" :show="true" :map="reactMap.map" :getDataByEquipId='getDataByEquipId' :areaQuery="true"
      :popTrackClick="popTrackClick">
    </mapTool>

    <div class="map-graph-edit">
      <mapGraphEdit ref="myEditMap" editType="polygon" :selectGraph="[]"></mapGraphEdit>
    </div>

    <!-- 右侧设备树 -->
    <div class="equip-tree" id="realtimeCarTree">
      <div class="move-img" v-treeMove>
      </div>
      <statisticDom :data="allData" :resetData="resetData" :dataSource='dataSource'></statisticDom>
      <realtimeTree ref="carTree" :videoClick="showVideoPage" :treeList="treeList" :defaultCheckedNode="treeCheckedKeys"
        :rightMenuClick="handleClick" :handleCheckChange="nodeCheck" :nodeClick="getCarInfo" :dataRefresh="refreshMapData"
        style="height:calc(100% - 68px)"></realtimeTree>
    </div>

    <!-- 下侧状态表格 -->
    <status-table class="status-table" :baseData='baseData' :checkedData="checkedData" :alarmList="alarmList"
      :tableHandle="tableHandle" :tableShrink="tableShrink" :rowClick="getCarInfo" :realtimeTableShow="realtimeTableShow"
      ref="statusTable"></status-table>

    <!-- 设备视频播放 -->
    <el-dialog title="监管对象视频" v-model="showVideo" :destroy-on-close="true" class="video-dialog" @close="videoClose">
      <videoPage :videoMaxNum="videoMaxNum" :videoList="videoList" :videoHash="videoHash"></videoPage>
    </el-dialog>

    <!-- 设备详情页面 -->
    <detailInfo class="car-detail-dom" v-if="showDetailPage" :closeDetailPage="closeDetailPage" :detailCar="detailCar"
      :spdLineData='spdLineData' ref="myDetailInfo" :resizeMap="resizeMap">
    </detailInfo>

    <!-- 处理树右键的页面 -->
    <infoEdit :rightMenuData="rightMenuData" :refreshMapData="refreshMapData"></infoEdit>
  </div>
</template>
<script lang="ts">
import mapGraphEdit from "../../../components/map/mapGraphEdit.vue"
import flvjs from "flv.js"
declare const L: any;
declare const GlobalSetting: any;
let markerIndex = 100, rpMap: any = null;
let carMapMarker: any = null; //详情地图里面的标注
let carMap: any = null;
let trackingCar: any = {}; //跟踪设备号
let thePop: any = null;
import { myWebSocket } from '../../../utils/websocket';
import dayjs from "dayjs";
import { initMap } from "../../../utils/map/leafletMap";
import { TreeCreate, countLeaves } from '../../../utils/createTree'
import mapPop from "./realtimeLocPop.vue"
import { getLastGpsByid, getCarVideoList, getSearchCompanyList, getMapCar } from "../../../api/trajectoryMonitor/realtimePosition";
import axios from 'axios';
import { ref, Ref, nextTick, createApp, toRaw } from "vue"
import { ElNotification } from "element-plus"
import mapTool from "../../../components/map/mapTool.vue"
import "leaflet.markercluster/dist/MarkerCluster.Default.css"
import "leaflet.markercluster/dist/MarkerCluster.css"
import "leaflet.markercluster";
import "../../../utils/map/Leaflet.Marker.SlideTo"
import { useRouter } from 'vue-router';
import { delDept } from "../../../api/system/dept";
import statisticDom from "./statisticDom.vue"
import { delEquipmentManagement } from "../../../api/comlbs/equipmentManagement";
//@ts-ignore
import statusTable from "./statusTable.vue"
import detailInfo from "./detailInfo.vue"
import videoPage from "./videoPage.vue"
import infoEdit from "./infoEdit.vue"
let markerCluster: any = null;//聚合marker的图层
const _hash: any = GlobalSetting.wordTransfer;
let curPagePath = null;//记录当前页面的路径
import realtimeTree from "./realtimeTree.vue";
let trackCarMoveLineLayer: any = null;
let trackCarMovePointLayer: any = null;
let mapSettings = JSON.parse(localStorage.getItem('basicSettings'))
let websocket: any = null;
let MapPopContent: any = null;
let timer: any = null;
let actived = true;
export default {
  name: "RealtimePosition",
  setup() {
    const allCarHash: any = ref({});//所有设备hash表，里面结构为{data,marker}
    let router = useRouter();
    const carTree: any = ref();
    const statusTable: any = ref();
    const legendShrink: any = ref(false);
    const dataSource: any = ref('all');
    function myRequire(str) {
      return new URL(`../../../assets/img/${str}`, import.meta.url).href
    }
    const detailCar: any = ref({}); //当前展示设备（设备详情）  
    const tableShrink = ref(true);
    const backImgHash: any = ref({
      "行驶": myRequire("map/pop/driving.png"),
      "停留": myRequire("map/pop/stop.png"),
      "熄火": myRequire("map/pop/flameout.png"),
      "定位失败": myRequire("map/pop/positioningFail.png"),
      "离线": myRequire("map/pop/offline.png"),
    })
    const mapLoading: any = ref(true);
    const showVideo: any = ref(false);
    const realtimeTableShow: any = ref(true);
    const videoMaxNum = ref(1);//当前展示视频
    const videoHash: any = ref({});
    const showDetailPage: any = ref(false);
    const spdLineData: any = ref({
      xAxisData: [],
      yAxisData: [],
    });
    const preCar: any = ref({});
    const companyList: any = ref([]);
    const videoList = ref([]);//当前设备视频列表
    const treeCheckedKeys: Ref<Array<string>> = ref(["0"]);//设备树勾选的id
    const reactMap: any = ref({ map: null });
    const rightMenuData = ref({});
    const countDown = ref(Number(mapSettings.mapFlushSequence));
    const myDetailInfo = ref(null)
    return {
      statusTable,
      allCarHash,
      realtimeTableShow,
      dataSource,
      myRequire,
      carTree,
      legendShrink,
      backImgHash,
      mapLoading,
      showVideo,//视频页面控制参数
      videoList,
      videoMaxNum,
      videoHash,//十六个视频关联的摄像头  
      showDetailPage,
      spdLineData,//详情左下角echart图data   
      preCar,
      companyList,
      treeCheckedKeys,//设备树勾选的id
      mapRefresh: Number(mapSettings.mapFlushSequence),
      mapCenterLng: GlobalSetting.MapConfig.center.lng,
      mapCenterLat: GlobalSetting.MapConfig.center.lat,
      reactMap,
      router,
      tableShrink,
      detailCar,
      rightMenuData,
      countDown,
      myDetailInfo
    };
  },
  created() {
    //websocket通信连接
    const wsUrl = GlobalSetting.IotConfig.WebSocketUrl;
    websocket = new myWebSocket(wsUrl);
    document.documentElement.style.setProperty('--realtime-table-height', '34px')
    document.documentElement.style.setProperty('--realtime-tree-width', '300px')
  },
  mounted() {
    let _this = this;
    // 地图初始化
    initMap("rpMap").then((backMap: any) => {
      rpMap = backMap;
      _this.refreshMapData(true);
      _this.reactMap.map = rpMap;
      // 引入 Vue 组件构造器实例化
      let mapPopContent = createApp(mapPop)
      let parent = document.createElement('div');

      // 挂载组件
      MapPopContent = mapPopContent.mount(parent);
      MapPopContent.parent = _this;

      L.control.zoom({
        position: "bottomleft",
        zoomInTitle: "放大",
        zoomOutTitle: "缩小"
      }).addTo(rpMap);

      this.setViewMap()
    });
  },
  computed: {
    // 用来判断哪些为空数组，哪些塞数据进去
    baseParam() {
      let hash = {
        online: ['driving', "stop", "flameout", "positioningFail"],
        offline: ["offline"],
      };
      let dataList = [], zeroList = [];
      if (this.dataSource == 'all') {
        dataList = [...hash.online, ...hash.offline];
        zeroList = [];
      } else if (this.dataSource == 'online') {
        dataList = hash.online;
        zeroList = hash.offline;
      } else if (this.dataSource == 'offline') {
        dataList = hash.offline;
        zeroList = hash.online;
      }
      return {
        dataList, zeroList
      }
    },
    allData() {
      // 数据分类
      let list = {
        driving: [],  //行驶-在线
        stop: [], //停留-在线
        flameout: [], //熄火-在线
        positioningFail: [], //定位失败-在线
        offline: [], //离线-离线
        // notLocate: [], //未定位-离线
      }
      for (let key in this.allCarHash) {
        let _data = this.allCarHash[key].data
        if (!list[_hash[_data.vehicleStatus]]) console.log(_data)
        else list[_hash[_data.vehicleStatus]].push(_data);
      }
      return list;
    },
    // 拿到分类后数据的数组
    allSortList() {
      let list = [];
      this.baseParam.dataList.forEach(v => {
        list = [...list, ...this.allData[v]]
      })
      return list;
    },
    // 生成树
    treeList() {
      let treeBaseData = [...this.companyList, ...this.allSortList];
      let newTree = new TreeCreate(treeBaseData,
        {
          id2: "deviceNo",
          pId2: 'companyId',
        })
      let list = [newTree.getData()]
      list.forEach(v => {
        countLeaves(null, v)
      })
      return list;
    },
    // 根据右上角的参数确定基础数据源
    baseData() {
      // 数据分类
      let newData = {
        driving: [],  //行驶-在线
        stop: [], //停留-在线
        flameout: [], //熄火-在线
        positioningFail: [], //定位失败-在线
        offline: [], //离线-离线
        // notLocate: [], //未定位-离线
      }
      // 树筛选
      let keyHash = {};
      this.treeCheckedKeys.forEach(v => {
        keyHash[v] = true;
      })
      this.allSortList.forEach(v => {
        if (this.treeCheckedKeys[0] == '0') {
          newData[_hash[v.vehicleStatus]].push(v);
        } else if (keyHash[v.deviceNo]) {
          newData[_hash[v.vehicleStatus]].push(v);
        }
      })
      return newData;
    },
    // 当前被勾选的数据
    checkedData() {
      let list = [];
      for (let key in this.baseData) {
        list = [...list, ...this.baseData[key]]
      }
      return list;
    },
    // 报警设备列表
    alarmList() {
      let list = [];
      this.checkedData.forEach(v => {
        if (v.hasAlarm) list.push(v)
      })
      return list;
    },
  },
  components: {
    realtimeTree, mapTool, statisticDom, statusTable, detailInfo, videoPage, infoEdit, mapGraphEdit
  },
  methods: {
    combineData(obj, objNext) {
      // 谁的时间大，就用谁的覆盖
      let first = obj, next = objNext;
      if (obj.gpsDateTime > objNext.gpsDateTime) {
        first = objNext;
        next = obj;
      }
      let newObj = {};
      for (let key in first) {
        if (first.hasOwnProperty(key)) {
          newObj[key] = first[key];
        }
      }
      for (let key in next) {
        if (next.hasOwnProperty(key)) {
          newObj[key] = next[key];
        }
      }
      return newObj;
    },
    deepCopy(obj) {
      if (obj === null || typeof obj !== 'object') {
        return obj;
      }
      let copy = Array.isArray(obj) ? [] : {};
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          copy[key] = this.deepCopy(obj[key]);
        }
      }
      return copy;
    },
    getDataByEquipId(deviceNo) {
      return this.allCarHash[deviceNo]?.data
    },
    tableHandle(param, data, str) {
      switch (param) {
        case "toggle":
          document.documentElement.style.setProperty('--realtime-table-height', this.tableShrink ? '276px' : '34px');
          this.tableShrink = !this.tableShrink;
          this.$nextTick(() => {
            this.showCarTree();
            rpMap?.invalidateSize(false);
          })
          break
        case "export":
          let ids = data.map(v => v.deviceNo).join();
          this.download('/comlbs/realtimePositioning/deviceExport', {
            deviceIds: ids
          }, `实时定位-${str}_${dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss")}.xlsx`);
          break
      }
    },
    resetData(param) {
      let _this = this;
      if (this.dataSource == param) return
      // 重置树勾选为全命中
      this.dataSource = param;
      this.carTree.filterText = '';
      this.$nextTick(() => {
        //地图设备图标控制
        let markerList: any = [];
        _this.closePop();
        let list = [];
        for (let key in _this.baseData) {
          list = list.concat(_this.baseData[key])
        }
        list.forEach((v: any) => {
          let _marker = toRaw(this.allCarHash[v.deviceNo].marker)
          if (_marker) markerList.push(_marker)
        })
        _this.initMapConfig(markerList, 'reset');
      })
      setTimeout(() => {
        this.$nextTick(() => {
          // 先全选
          this.carTree.realtimeTree.setCheckedKeys(['0']);
          this.$nextTick(() => {
            // 然后设置勾选id
            this.treeCheckedKeys = this.carTree.realtimeTree.getCheckedKeys();
          })
        })
      }, 1)
    },
    showCarTree() {
      setTimeout(() => {
        nextTick(() => {
          this.carTree?.realtimeTree.setCheckedKeys(this.treeCheckedKeys);
        })
      }, 1);
    },
    // 树右键点击事件
    handleClick(param: string, data: any) {
      let _this = this;
      function setSonPageData() {
        _this.rightMenuData = {
          param, data
        }
      }
      switch (param) {
        case "trajectory":
          this.popTrackClick(data);
          break
        case "detail":
          this.showCarDetail(data);
          break
        case "createChild": //创建子节点
          setSonPageData();
          break
        case "rename": //重命名
          setSonPageData();
          break
        case "addEquip": //新增设备
          setSonPageData();
          break
        case "changeCompany": //迁移节点
          setSonPageData();
          break
        case "deleteCompany": //删除节点
          this.$modal.confirm('是否确认删除名称为“' + data.label + '”的数据项?').then(function () {
            return delDept(data.id);
          }).then(() => {
            this.refreshMapData();
            this.$modal.msgSuccess("删除成功");
          }).catch(() => { });
          break
        case "bindRules": //已绑规则
          setSonPageData();
          break
        case "changeEquip": //迁移设备
          setSonPageData();
          break
        case "deleteEquip": //删除设备
          this.$modal.confirm('是否确认删除名称为“' + data.numberPlate + '”的数据项?').then(function () {
            return delEquipmentManagement(data.id);
          }).then(() => {
            this.refreshMapData();
            this.$modal.msgSuccess("删除成功");
          }).catch(() => { });
          break
      }
    },
    getPopImg(obj: any, isActive: Boolean = false) {
      let _src, _backSrc;
      _backSrc = this.backImgHash[obj.vehicleStatus]
      _src = this.myRequire('equipment/' + obj.objIcon + '.png')
      let activeStr = ``
      if (isActive) {
        activeStr = `<div class='outer-acitve-shadow'></div>`
      }
      return L.divIcon({
        html: `<img src="${_src}" class='map-icon'/>` +
          `<img src="${_backSrc}" class='map-icon-back' style="transform:rotate(${obj.dre - 45}deg)"/>` +
          activeStr +
          ` <div class='map-text'><div class='map-triangle'></div>${obj.numberPlate}</div>`,
      })
    },
    //activeRefresh：是否主动刷新
    refreshMapData(activeRefresh: boolean) {
      if (activeRefresh) {
        this.mapLoading = true;  // 定时刷新不要loading
        this.createSetInterval();
      }
      this.initData();
    },
    closeDetailPage() {
      this.clearMapFeature();
      this.showDetailPage = false;
      carMap = null;
      carMapMarker = null;
      this.detailCar = {};
      this.cancelTrack();
    },
    closePop() {
      rpMap.closePopup();
      this.carTree.realtimeTree.setCurrentKey(null);//选中树节点
      this.statusTable.$refs.v2Table.setActiveKey(null);//选中表格行
      thePop = null;
      // 如果没有大屏
      if (!this.showDetailPage) {
        this.detailCar = {};
      }
    },
    // 清除地图覆盖物
    clearMapFeature() {
      trackCarMoveLineLayer?.setLatLngs([]);
      trackCarMovePointLayer?.clearLayers();
    },
    cancelTrack() {
      this.getSocketData('type=2&dvo=0');
      let _marker = toRaw(this.allCarHash[trackingCar.deviceNo].marker)
      _marker.setIcon(this.getPopImg(this.allCarHash[trackingCar.deviceNo].data));
      trackingCar.deviceNo = null;
    },
    // 跟踪设备
    equipTrack(obj: any) {
      this.clearMapFeature();
      if (trackingCar.deviceNo == obj.deviceNo) {
        this.cancelTrack();
        return
      }
      //socket连接
      this.detailCar = this.deepCopy(obj);
      trackingCar = this.deepCopy(obj);
      trackingCar.deviceNo = obj.deviceNo;
      this.getSocketData('type=2&dvo=' + obj.deviceNo);
      this.getCarInfo(obj)
    },
    videoClose() {
      for (let i = 0; i < 16; i++) {
        this.closeVideo('video' + i);
      }
    },
    closeVideo(key) {
      if (!this.videoHash[key].dom && !this.videoHash[key].title && !this.videoHash[key].id) return
      if (this.videoHash[key].dom) {
        this.videoHash[key].dom.pause()
        this.videoHash[key].dom.unload()
        this.videoHash[key].dom.detachMediaElement()
        this.videoHash[key].dom.destroy()
        this.videoHash[key].dom = null;
      }
      this.videoHash[key].title = null;
      this.videoHash[key].id = null;
    },
    // 查看视频
    showVideoPage(obj: any) {
      if (!this.getIsOnline(obj.vehicleStatus)) return
      let _this = this;
      _this.showVideo = true
      for (let i = 0; i <= 15; i++) {
        _this.videoHash['video' + i] = {
          title: null,
          dom: null,
          id: null
        }
      }
      getCarVideoList({
        deviceId: obj.deviceNo,
      }).then((result: any) => {
        _this.videoList = result.data // 通道数组
        function autoGetCurrentNum(length: number) {
          let list = [1, 2, 4, 6, 9, 16];
          let i = 0;
          while (list[i] < length) {
            i++
          }
          return list[i]
        }
        // 如果当前视频数目不够播放则扩展到下一级别
        _this.videoMaxNum = autoGetCurrentNum(_this.videoList.length);

        // 修改屏幕数
        _this.videoList.forEach((v, i) => {
          let key = 'video' + i;
          _this.playSelectVideo(key, obj.deviceNo + '_' + v + '.media')
        })
      })
    },
    playSelectVideo(domId: string, url: string) {
      let _this = this;
      if (flvjs.isSupported()) {
        let videoElement, videoObj;
        videoElement = document.getElementById(domId);
        videoObj = flvjs.createPlayer({
          type: 'flv',
          isLive: true,
          // hasAudio: false,
          url: GlobalSetting.IotConfig.videoWS + url,
        }, {
          enableWorker: false, //不启用分离线程
          enableStashBuffer: false, //关闭IO隐藏缓冲区
          reuseRedirectedURL: true, //重用301/302重定向url，用于随后的请求，如查找、重新连接等。
          autoCleanupSourceBuffer: true, //自动清除缓存
          stashInitialSize: 500,// 减少首桢显示等待时长
        });

        _this.videoHash[domId].dom = videoObj

        videoObj.attachMediaElement(videoElement);
        videoObj.load();
        videoObj.play();

        //定时方法是为了用户离开页面视频是实时播放的,暂停按钮无用
        setInterval(function () {
          if (videoElement.buffered.length > 0 && !actived) {
            const end = videoElement.buffered.end(0); // 视频结尾时间
            const current = videoElement.currentTime; //  视频当前时间
            const diff = end - current; // 相差时间

            const diffCritical = 4; // 这里设定了超过4秒以上就进行跳转
            const diffSpeedUp = 1; // 这里设置了超过1秒以上则进行视频加速播放
            const maxPlaybackRate = 4; // 自定义设置允许的最大播放速度
            let playbackRate = 1.0; // 播放速度
            if (diff > diffCritical) {
              //  flvPlayer.load();
              videoElement.currentTime = end - 1.5;
              playbackRate = Math.max(1, Math.min(diffCritical, 16));
            } else if (diff > diffSpeedUp) {
              playbackRate = Math.max(1, Math.min(diff, maxPlaybackRate, 16));
            }
            videoElement.playbackRate = playbackRate;
            if (videoElement.paused) {
              videoElement.play();
            }
          }
        }, 0);
      } else {
        console.log("暂不支持flv.js")
      }
    },
    //格式化时间
    format(dat) {
      //获取年月日，时间
      let year = dat.getFullYear();
      let mon = (dat.getMonth() + 1) < 10 ? "0" + (dat.getMonth() + 1) : dat.getMonth() + 1;
      let data = dat.getDate() < 10 ? "0" + (dat.getDate()) : dat.getDate();
      let hour = dat.getHours() < 10 ? "0" + (dat.getHours()) : dat.getHours();
      let min = dat.getMinutes() < 10 ? "0" + (dat.getMinutes()) : dat.getMinutes();
      let seon = dat.getSeconds() < 10 ? "0" + (dat.getSeconds()) : dat.getSeconds();

      let newDate = year + "-" + mon + "-" + data + " " + hour + ":" + min + ":" + seon;
      return newDate;
    },
    // 树节点勾选
    nodeCheck(obj: any, checked: boolean) {
      let handle = '', _this = this;
      if (checked) {
        handle = 'show'
      } else {
        handle = 'hide';
        // 关闭气泡
        rpMap.closePopup();
      }
      _this.treeCheckedKeys = _this.carTree.realtimeTree.getCheckedKeys();

      let diffList: Array<any> = [];
      function getCarsDevideNo(_obj: any) {
        if (_obj.type === 2) {//说明是车
          diffList.push(toRaw(_this.allCarHash[_obj.deviceNo].marker))
        } else {
          if (_obj.children?.length > 0) {
            _obj.children.forEach((v: any) => {
              getCarsDevideNo(v)
            })
          }
        }
      }
      getCarsDevideNo(obj)
      this.initMapConfig(diffList, handle);
    },
    // 判断是否在线
    getIsOnline(string: string) {
      return GlobalSetting.onlineStatus.some(v => { return v == string })
    },
    // 获取所有设备数据
    initData() {
      let _this = this;
      axios.all([getSearchCompanyList(), getMapCar()]).then(([res1, res2]) => {
        // 前端创建树
        _this.companyList = res1.data;
        // 聚合数据构建
        let _carList = [], _list: Array<any> = [], _marker;
        _carList = res2.data
        _carList.forEach((v: any) => {
          // 如果当前数据要比后端返回数据新
          if (this.allCarHash[v.deviceNo]?.data.gpsDateTime > v.gpsDateTime) {
            _marker = toRaw(this.allCarHash[v.deviceNo].marker);
          } else {
            _marker = L.marker(L.latLng(v), {
              icon: _this.getPopImg(v),
              zIndexOffset: markerIndex
            }).on('click', function () {
              _this.getCarInfo(v, false)
            });
            this.allCarHash[v.deviceNo] = {
              data: v,
              marker: _marker
            }
          }

          if (_this.treeCheckedKeys.length == 1 && _this.treeCheckedKeys[0] == '0') {
            _list.push(_marker)
          } else {
            if (_this.treeCheckedKeys.indexOf(v.deviceNo) > -1) _list.push(_marker)
          }
        });

        // 如果气泡存在，更新气泡
        if (thePop && this.detailCar && Object.keys(this.detailCar).length > 0 && this.allCarHash[this.detailCar.deviceNo].data.gpsDateTime > this.detailCar.gpsDateTime) {
          let _data = this.allCarHash[this.detailCar.deviceNo].data;
          this.detailCar = _this.combineData(this.detailCar, _data);
          thePop.setLatLng(this.detailCar)
          MapPopContent.getPopData(this.detailCar, trackingCar.deviceNo);
        }

        _this.initMapConfig(_list, 'reset');

        _this.showCarTree();

        _this.mapLoading && ElNotification({
          message: "刷新成功！",
          type: "success",
          position: "bottom-right",
          duration: 2000,
        });
        _this.mapLoading = false;
      })
    },
    // 加载地图图标
    initMapConfig(markerList: any, handle = 'show') {
      // 先移除再加载
      if (handle == 'reset') {
        markerCluster?.removeFrom(rpMap)
        //@ts-ignore
        markerCluster = L.markerClusterGroup({
          chunkedLoading: true,
          showCoverageOnHover: false,
          disableClusteringAtZoom: 17,
          spiderfyOnMaxZoom: false
        }).addTo(rpMap);
        this.$nextTick(() => {
          markerCluster.addLayers(markerList);
        })
      }
      if (handle === 'show') {
        markerCluster.addLayers(markerList);
      } else if (handle === 'hide') {
        markerCluster.removeLayers(markerList);
      }
    },
    // 移动图标，更新数据
    changeMarker(obj: any, param: string) {
      let _this = this;
      let _marker = toRaw(this.allCarHash[obj.deviceNo].marker)
      _marker.setIcon(_this.getPopImg(obj, trackingCar.deviceNo == obj.deviceNo));
      this.allCarHash[obj.deviceNo].data.vehicleStatus = obj.vehicleStatus
      this.allCarHash[obj.deviceNo].data.gpsDateTime = obj.gpsDateTime

      if (param == 'change') {
        _marker.setLatLng(obj);
      } else if (param == 'move') {
        rpMap.flyTo(obj, 17, { animate: false })
        this.$nextTick(() => {
          setTimeout(() => {
            _marker.slideTo(obj, { duration: 1000 });
            thePop?.slideTo(obj, { duration: 1000 });
          }, 100)
        })
      }
      if (_this.preCar.deviceNo) {
        //上一个图标层级回归默认
        toRaw(this.allCarHash[_this.preCar.deviceNo].marker).setZIndexOffset(0);
      }
      _this.preCar = obj
      _marker.setZIndexOffset(2000); //设置图标最上层
    },
    // 大屏幕展示详情
    showCarDetail(obj: any, pageStr) {
      let _this = this;
      rpMap.closePopup();
      this.clearMapFeature();
      this.showDetailPage = true
      this.detailCar = _this.deepCopy(obj)
      trackingCar = _this.deepCopy(obj)
      this.getSocketData('type=2&dvo=' + obj.deviceNo);
      this.$nextTick(() => {
        if (pageStr) this.myDetailInfo.activeName = pageStr;
        this.myDetailInfo.tablePagination.currentPage = 1;
        this.myDetailInfo.getTableData();
      })
      getLastGpsByid(obj.deviceNo).then((response: any) => {
        function setMapMarker() {
          _this.detailCar = _this.combineData(obj, response.data)
          carMap.flyTo(_this.detailCar, 17, { animate: false })
          if (carMapMarker) {
            carMapMarker.removeFrom(carMap)
          }
          carMapMarker = L.marker(_this.detailCar, {
            icon: _this.getPopImg(_this.detailCar, true),
            zIndexOffset: markerIndex
          }).addTo(carMap);
        }
        _this.spdLineData = {
          xAxisData: [response.data.gpsDateTime],
          yAxisData: [response.data.spd],
        }
        if (carMap) {
          setMapMarker();
        } else {
          this.$nextTick(() => {
            initMap("carDetailMap").then(_map => {
              carMap = _map;
              setMapMarker();
            })
          })
        }
      })
    },
    // 获取设备详情信息,能够获取设备详情应该显示在地图上
    getCarInfo(_car: any, flyBool = true) {
      let _this = this;
      _this.carTree.realtimeTree.setCurrentKey(_car.deviceNo);//选中树节点
      _this.carTree.realtimeTree.setChecked(_car.deviceNo, true);//勾选树节点
      _this.nodeCheck(_car, true);//选中树节点手动触发勾选事件
      _this.statusTable.$refs.v2Table.setActiveKey(_car.deviceNo);//选中表格行
      //如果存在左侧详情窗体，则切换详情
      if (this.showDetailPage) {
        this.showCarDetail(_car)
        return
      }
      getLastGpsByid(_car.deviceNo).then((response: any) => {
        if (response.code == 200) {
          // 更新地图数据
          this.detailCar = _this.combineData(_car, response.data);
          // deviceNo和deviceId是一个意思
          this.detailCar.deviceNo = response.data.deviceId;
          this.detailCar.objType = _car.objType;
          if (Array.isArray(this.detailCar.vehiclePic) && this.detailCar.vehiclePic.length) {
            this.detailCar.imgURL = this.detailCar.vehiclePic[0].path;
          } else {
            this.detailCar.imgURL = null;
          }
          if (flyBool) {
            rpMap.flyTo(this.detailCar, 18, { animate: false });
          }
          // 没有fly说明点击的地图图标
          _this.changeMarker(this.detailCar, 'change');
          MapPopContent.getPopData(_this.detailCar, trackingCar.deviceNo);
          thePop = L.popup({
            maxWidth: undefined,
            closeButton: false,
            offset: L.point(0, -10),
            autoPanPaddingBottomRight: L.point(280, 0),
          }).setLatLng(_this.detailCar).setContent(MapPopContent.$el).openOn(rpMap);

          // 如果跟踪设备的轨迹线不存在
          if (!trackCarMoveLineLayer && trackingCar.deviceNo == this.detailCar.deviceNo) {
            trackingCar = JSON.parse(JSON.stringify(this.detailCar));
            trackCarMoveLineLayer = L.polyline([[trackingCar.lat, trackingCar.lng]], {
              weight: 4,
              color: 'blue'
            }).addTo(rpMap);
            trackCarMovePointLayer = L.featureGroup([
              L.circleMarker([trackingCar.lat, trackingCar.lng], {
                fill: true,
                fillColor: '#fff',
                radius: 5,
                weight: 2,
                opacity: 1,
                fillOpacity: 1
              })
            ]).addTo(rpMap)
          }

          // 更新下侧列表数据
          this.allCarHash[this.detailCar.deviceNo].data.gpsDateTime = this.detailCar.gpsDateTime;
          this.allCarHash[this.detailCar.deviceNo].data.spd = this.detailCar.spd;
          this.allCarHash[this.detailCar.deviceNo].data.dayKMeter = this.detailCar.dayKMeter;
          this.allCarHash[this.detailCar.deviceNo].data.kilometer = this.detailCar.kilometer;
          this.allCarHash[this.detailCar.deviceNo].data.location = this.detailCar.location;
          this.allCarHash[this.detailCar.deviceNo].data.satelliteCount = this.detailCar.satelliteCount;
          if (this.detailCar.spd == 0) {
            this.allCarHash[this.detailCar.deviceNo].data.remark = this.detailCar.vehicleStatus;
          } else {
            this.allCarHash[this.detailCar.deviceNo].data.remark = this.detailCar.spd + 'km/h';
          }
        }
      })
    },
    getSocketData(message: string) {
      let _this = this;
      websocket.sendMsg(message, (ev: any) => {
        let newCar = JSON.parse(ev.data);
        trackCarMoveLineLayer?.addLatLng({
          lng: newCar.lng,
          lat: newCar.lat,
        });
        let _marker = L.circleMarker([newCar.lat, newCar.lng], {
          fill: true,
          fillColor: '#fff',
          radius: 5,
          weight: 2,
          opacity: 1,
          fillOpacity: 1
        });
        trackCarMovePointLayer?.addLayer(_marker);
        // 获取状态
        function getCarStatus() {
          if (!newCar.sts.includes(2)) {
            return "定位失败"
          }
          if (!newCar.sts.includes(1)) {
            return "熄火"
          }
          if (newCar.spd) {
            return "行驶"
          } else {
            return "停留"
          }
        }
        trackingCar.spd = newCar.spd;
        trackingCar.lng = newCar.lng;
        trackingCar.lat = newCar.lat;
        trackingCar.dre = newCar.dre;
        trackingCar.gpsDateTime = newCar.gdt;
        trackingCar.kilometer = newCar.kilometer;
        trackingCar.vehicleStatus = getCarStatus();
        trackingCar.location = newCar.poi.ars;
        trackingCar.acc = newCar.sts.includes(1);//acc
        trackingCar.turnLeft = newCar.sts.includes(11);//左转
        trackingCar.turnRight = newCar.sts.includes(12);//右转
        trackingCar.highBeam = newCar.sts.includes(13);//远光灯
        trackingCar.dippedHeadlight = newCar.sts.includes(14);//近光灯
        trackingCar.oil = newCar.sts.includes(7);//油路
        trackingCar.brake = newCar.sts.includes(10);//刹车
        if (newCar.pop[251] !== undefined) trackingCar.satelliteCount = newCar.pop[251];//卫星数

        _this.changeMarker(trackingCar, 'move');

        // 如果存在详情弹窗，则对弹窗更新
        if (this.showDetailPage && this.detailCar.deviceNo == newCar.dvo) {
          this.spdLineData.xAxisData.push(newCar.gdt);
          this.spdLineData.yAxisData.push(newCar.spd);
          let icon = _this.getPopImg(this.detailCar, true);
          if (!carMap) return
          if (carMapMarker) {
            carMapMarker.setIcon(icon);
            carMapMarker.slideTo(this.detailCar, { duration: 1000 });
            carMap.flyTo(this.detailCar, carMap.getZoom(), { animate: false })
          } else {
            carMapMarker = L.marker(this.detailCar, {
              icon: icon,
              zIndexOffset: markerIndex
            }).addTo(carMap);
            carMap.flyTo(this.detailCar, 17, { animate: false })
          }
        } else {
          // 如果存在详情的行为，更新数据
          // this.detailCar = JSON.parse(JSON.stringify(trackingCar));
          // MapPopContent.getPopData(this.detailCar, trackingCar.deviceNo);
        }
        // 如果存在详情的行为，更新数据
        this.detailCar = JSON.parse(JSON.stringify(trackingCar));
        MapPopContent.getPopData(this.detailCar, trackingCar.deviceNo);

        rpMap.setView([trackingCar.lat, trackingCar.lng])
      })
    },
    // 气泡的轨迹详情点击
    popTrackClick(data: any) {
      let queryTime = data.queryTime ? data.queryTime : [dayjs(new Date()).format("YYYY-MM-DD 00:00:00"), dayjs(new Date()).format("YYYY-MM-DD 23:59:59")];
      window.open(window.location.origin + "/trajectoryTrack?numberPlate=" + data.numberPlate + "&deviceNo=" + data.deviceNo + "&companyId=" + data.companyId + "&queryTime=" + queryTime)
    },
    // 开启轮询  如果存在则先销毁定时器后重新开启
    createSetInterval() {
      let _this = this
      _this.stopSetInterval()
      timer = setInterval(() => {
        _this.countDown--;
        if (_this.countDown + _this.mapRefresh == _this.mapRefresh) {
          _this.refreshMapData(false);
          _this.countDown = _this.mapRefresh
        }
      }, 1000)
    },
    // 关闭轮询
    stopSetInterval() {
      if (timer) {
        clearInterval(timer)
        timer = null
      }
    },
    setViewMap() {
      // 基础设置中设置了地图中心点,即定位到此;否则定位到武汉
      if (mapSettings.latitude && mapSettings.longitude) {
        this.$nextTick(() => {
          rpMap.setView([mapSettings.latitude, mapSettings.longitude]);
        })
      }
    },
    resizeMap() {
      carMap?.invalidateSize && carMap.invalidateSize(false);
    },
  },
  activated() {
    actived = true;
    rpMap?.invalidateSize(false);
    carMap?.invalidateSize && carMap.invalidateSize(false);
    this.createSetInterval();
    if (document.getElementsByClassName("el-tree-virtual-list")[0]) {
      document.getElementsByClassName("el-tree-virtual-list")[0].scrollTop = Number(localStorage.getItem("vlTreeScrollTop"));
    }
    this.realtimeTableShow = false;
    this.$nextTick(() => {
      this.realtimeTableShow = true;
    })
  },
  deactivated() {
    actived = false;
    this.stopSetInterval();
  },
  beforeUnmount() {
    rpMap?.remove();
    rpMap = null;
    markerCluster = null;
    if (websocket) {
      websocket.close();
    }
  },
  directives: {
    move(el) {
      el.onmousedown = function (e: any) {
        let init = e.clientY;
        //要移动的下侧窗体
        let parent: any = document.getElementById("movePage");
        let initHeight = parent.offsetHeight;

        let minHeight = 52;
        let maxHeight = 600;

        document.onmousemove = function (e) {
          let end = e.clientY;
          if (initHeight - end + init > minHeight && initHeight - end + init < maxHeight) parent.style.height = initHeight - end + init + "px"
          else if (initHeight - end + init < minHeight) parent.style.height = minHeight + 'px'
          else if (initHeight - end + init > maxHeight) parent.style.height = maxHeight + 'px'
        };
        document.onmouseup = function () {
          document.onmousemove = document.onmouseup = null;
        };
      };
    },
    treeMove(el) {
      el.onmousedown = function (e: any) {
        let init = e.clientX;

        //默认是该元素的父元素移动
        let parent = el.parentElement;
        let initWidth = parent.offsetWidth;
        document.onselectstart = () => {
          return false
        };
        document.ondragstart = () => {
          return false
        };

        let minWidth = 280;
        let maxWidth = 480;

        document.onmousemove = function (e) {
          let end = e.clientX;
          if (initWidth - end + init > minWidth && initWidth - end + init < maxWidth) {
            document.documentElement.style.setProperty('--realtime-tree-width', initWidth - end + init + "px")
          }
          rpMap?.invalidateSize(false);
        };
        document.onmouseup = function () {
          document.onselectstart = null;
          document.ondragstart = null;
          document.onmousemove = document.onmouseup = null;
        };
      };
    },
  },
  watch: {
    'router.currentRoute.value.path': {
      handler(val) {
        // 记录当前页面路径
        if (curPagePath == null) {
          curPagePath = val
        }
        // 当离开当前页面且滚动条存在的时候，记录当前位置
        if (document.getElementsByClassName("el-tree-virtual-list")[0] && curPagePath != val) {
          localStorage.setItem('vlTreeScrollTop', '' + document.getElementsByClassName("el-tree-virtual-list")[0].scrollTop)
        }
      },
      deep: true,
      immediate: true,
    }
  },
};
</script>
<style scoped lang='scss'>
.realtimePosition {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;

  .refresh-text {
    bottom: calc(var(--realtime-table-height) + 12px);
    left: 57px;
    position: absolute;
    background-color: #fff;
    box-shadow: 0px 2px 2px 0px #00000040;
    width: 84px;
    line-height: 30px;
    text-align: center;
    color: #223355;
    font-size: 14px;
    border-radius: 4px;
  }

  .status-table {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--realtime-table-height);
    background-color: #fff;
    z-index: 10;
    box-shadow: 0 -3px 10px 0 rgba(0, 0, 0, 0.1);
    overflow-x: auto;
    overflow-y: hidden;
  }

  .equip-tree {
    filter: drop-shadow(0px 12px 20px rgba(68, 77, 113, 0.3));
    position: absolute;
    right: 0;
    width: var(--realtime-tree-width);
    top: 0;
    bottom: var(--realtime-table-height);
    background: #fff;
    z-index: 6;

    .move-img {
      background: url("../../../assets/img/moving.png") no-repeat center;
      width: 10px;
      height: 100%;
      cursor: col-resize;
      position: absolute;
    }
  }

  .car-detail-dom {
    filter: drop-shadow(0px 12px 20px rgba(68, 77, 113, 0.3));
    position: absolute;
    left: 0;
    bottom: var(--realtime-table-height);
    top: 0;
    right: var(--realtime-tree-width);
    background: #fff;
    padding: 0 40px;
    z-index: 5;
  }

  .myMap {
    position: absolute;
    left: 0;
    top: 0;
    right: var(--realtime-tree-width);
    bottom: var(--realtime-table-height);
    z-index: 0;
  }

  .leftTopTool {
    filter: drop-shadow(0px 12px 20px rgba(68, 77, 113, 0.3));
    position: absolute;
    left: 10px;
    top: 10px;
  }

  :deep(.video-dialog) {
    width: fit-content;
    height: fit-content;
  }

  :deep(.success-tab) {
    background-color: rgb(5, 185, 47);
    color: #fff;
    padding: 0px 8px;
    border-radius: 4px;
    display: inline-block;
  }

  :deep(.failed-tab) {
    background-color: rgb(234, 87, 87);
    color: #fff;
    padding: 0px 8px;
    border-radius: 4px;
    display: inline-block;
  }
}

.map-graph-edit {
  position: absolute;
  padding: 20px;
  background-color: #fff;
  top: 100px;
  left: 200px;
  width: 800px;
  height: 600px;
  display: none;
}

:deep(.leaflet-div-icon) {
  background: transparent;
  border-color: transparent;
}
</style>
